﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MessageStore
{
    public class MessageFile: IDisposable
    {
        /// <summary>
        /// 文件内容地址
        /// </summary>
        public string MessageFilePath { get; private set; }

        /// <summary>
        /// 头文件坐标地址
        /// </summary>
        public string HeaderFilePath { get; private set; }

        /// <summary>
        /// 文件大小
        /// </summary>
        public long MaxMessageFileSize { get;  set; }

        /// <summary>
        /// 消息总数
        /// </summary>
        public long MessageCount { get; private set; }

        /// <summary>
        /// 消息文件当前写入地址
        /// </summary>
        private long CurrentWriteMessagePosition;

        /// <summary>
        /// 消息文件待写入地址
        /// </summary>
        private long NextWriteMessagePosition;

        /// <summary>
        /// 头文件当前写入地址
        /// </summary>
        private long CurrentWriteHeaderPosition;

        /// <summary>
        /// 头文件待写入地址
        /// </summary>
        private long NextWriteHeaderPosition;

        /// <summary>
        /// 头文件流
        /// </summary>
        private Stream HeaderStream;

        /// <summary>
        /// 消息文件流
        /// </summary>
        private Stream MessageStream;

        /// <summary>
        /// 消息坐标及长度占用字节数
        /// </summary>
        private const int HeaderRecorderSize = HeaderPositionSize + MessageLengthSize;
        private const int HeaderPositionSize = sizeof(long);
        private const int MessageLengthSize = sizeof(int);

        public MessageFile(string MessageFilePath, string HeaderFilePath, long MaxMessageFileSize = 1024*1024*1024)
        {
            this.MessageFilePath = MessageFilePath;
            this.HeaderFilePath = HeaderFilePath;
            this.MaxMessageFileSize = MaxMessageFileSize;
            OpenPositionFile();
            OpenMessageFile();
            ComputerCurrentStatus();
        }

        public Span<byte> ReadMessage(long index)
        {
            if (index < 0 || index >= MessageCount) throw new Exception("数据下标越界！");
            var header = ReadHeader(index);
            if (header.MessagePosition != MessageStream.Position) MessageStream.Seek(header.MessagePosition, SeekOrigin.Begin);
            Span<byte> datas = new byte[header.MessageLength];
            MessageStream.Read(datas);
            var startLength = BitConverter.ToInt32(datas.Slice(0, MessageLengthSize));
            var endLength = BitConverter.ToInt32(datas.Slice(datas.Length - MessageLengthSize));
            if (startLength != endLength) throw new Exception("数据前后长度校验失败，数据异常！");
            return datas.Slice(MessageLengthSize, datas.Length - MessageLengthSize * 2);
        }


        public bool WriteMessage(byte[] message)
        {
            var messageLength = message.Length + MessageLengthSize * 2;
            if (NextWriteMessagePosition + messageLength > MaxMessageFileSize) return false;  //数据文件已满

            if (MessageStream.Position != NextWriteMessagePosition) MessageStream.Seek(NextWriteMessagePosition, SeekOrigin.Begin);
            MessageStream.Write(BitConverter.GetBytes(message.Length));
            MessageStream.Write(message);
            MessageStream.Write(BitConverter.GetBytes(message.Length));
            MessageStream.Flush();
            CurrentWriteMessagePosition = NextWriteMessagePosition;
            NextWriteMessagePosition = CurrentWriteMessagePosition + messageLength;

            WriterHeader(CurrentWriteMessagePosition,messageLength);

            return true;
        }

        /// <summary>
        /// 写入头文件
        /// </summary>
        /// <param name="messagePosition"></param>
        /// <param name="messageLength"></param>
        private void WriterHeader(long messagePosition,int messageLength)
        {
            if (NextWriteHeaderPosition != HeaderStream.Position) HeaderStream.Seek(NextWriteHeaderPosition, SeekOrigin.Begin);
            HeaderStream.Write(BitConverter.GetBytes(messagePosition));
            HeaderStream.Write(BitConverter.GetBytes(messageLength));
            HeaderStream.Flush();
            CurrentWriteHeaderPosition = NextWriteHeaderPosition;
            NextWriteHeaderPosition = CurrentWriteHeaderPosition + HeaderRecorderSize;
            MessageCount++;
        }

        /// <summary>
        /// 读取头文件内容
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        private (long MessagePosition, int MessageLength) ReadHeader(long index)
        {
            (long, int) ret;           
            var readPosition = index * HeaderRecorderSize;
            if (HeaderStream.Position != readPosition) HeaderStream.Seek(readPosition, SeekOrigin.Begin);
            Span<byte> datas = new byte[HeaderRecorderSize];
            HeaderStream.Read(datas);
            ret.Item1 = BitConverter.ToInt64(datas.Slice(0, HeaderPositionSize));
            ret.Item2 = BitConverter.ToInt32(datas.Slice(HeaderPositionSize));
            return ret;
        }

        private void ComputerCurrentStatus()
        {
            MessageCount = HeaderStream.Length / (HeaderRecorderSize);
            if (MessageCount > 0)
            {
                CurrentWriteHeaderPosition = (MessageCount - 1) * HeaderRecorderSize;
                NextWriteHeaderPosition = MessageCount * HeaderRecorderSize;
                var header = ReadHeader(MessageCount - 1);
                CurrentWriteMessagePosition = header.MessagePosition;
                NextWriteMessagePosition = CurrentWriteMessagePosition + header.MessageLength;
            }
        }


        private void OpenPositionFile()
        {
            if (HeaderStream == null)
            {
                HeaderStream=new FileStream(HeaderFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);

            }            
        }

        private void OpenMessageFile()
        {
            if (MessageStream == null)
            {
                if (File.Exists(MessageFilePath))
                {
                    MessageStream = new FileStream(MessageFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
                }
                else
                {
                    MessageStream = new FileStream(MessageFilePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read);
                    MessageStream.SetLength(MaxMessageFileSize);
                }
            }
        }

        #region 析构函数

        private bool isdisposed;

        public void Close()
        {
            Dispose();
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (isdisposed == false)
            {
                if (disposing)  //释放托管资源
                {
                    if (MessageStream != null) MessageStream.Dispose();
                    if (HeaderStream != null) HeaderStream.Dispose();
                }
                //释放非托管资源
                //do something

                isdisposed = true;
            }
        }

        ~MessageFile()
        {
            Dispose(false);
        }

        #endregion

    }
}
